package com.wushijia.highconcurrency.future;

import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * 响应结果或异常
 */
public class CompletableFutureTest1 {

  public static void main(String[] args) {
//    handlerTest();
//    whenCompleteTest();
//    runAsyncTest();
//    thenCombineAsyncTest();
    allOfTest();
  }

  /**
   * 异步任务抛出了异常，但通过handle方法，改变了结果
   */
  private static void handlerTest() {
    String str = CompletableFuture.supplyAsync(() -> {
      throw new RuntimeException("test");
    }).handle((result, ex) -> "hello").join();
    System.out.println(str);
  }

  /**
   * whenComplete只是注册回调函数，不改变结果
   */
  private static void whenCompleteTest() {
    CompletableFuture.supplyAsync(() -> {
      throw new RuntimeException("test");
    }).whenComplete((result, ex) -> {
      if (result != null) {
        System.out.println(result);
      }
      if (ex != null) {
        ex.printStackTrace();
      }
    });
  }

  //如果任务异常结束了，join会将异常包装为运行时异常CompletionException抛出。
  private static void runAsyncTest() {
    CompletableFuture.runAsync(() -> {
      System.out.println("taskA");
    }).thenRun(() -> {
      System.out.println("taskB");
    }).thenRun(() -> {
      System.out.println("taskC");
    }).join();
  }

  //thenCompose与thenAccept的关系就如同flatMap与map的关系，thenCompose起到转换的作用
  private static void thenComposeTest() {
    Supplier<String> taskA = () -> "hello";
    Function<String, CompletableFuture<String>> taskB = (t) -> CompletableFuture.supplyAsync(t::toUpperCase);
    Consumer<String> taskC = (t) -> System.out.println("consumer: " + t);
    CompletableFuture.supplyAsync(taskA).thenCompose(taskB).thenAccept(taskC).join();
  }

  private static void thenCombineAsyncTest() {
    Supplier<String> taskA = () -> "taskA";
    CompletableFuture<String> taskB = CompletableFuture.supplyAsync(() -> "taskB");
    BiFunction<String, String, String> taskC = (a, b) -> a + "," + b;
    String ret = CompletableFuture.supplyAsync(taskA)
        .thenCombineAsync(taskB, taskC)
        .join();
    System.out.println(ret);
  }

  private static void allOfTest() {
    CompletableFuture<String> taskA = CompletableFuture.supplyAsync(() -> {
      delayRandom(100, 1000);
      return "helloA";
    }, executor);
    CompletableFuture<Void> taskB = CompletableFuture.runAsync(() -> {
      delayRandom(2000, 3000);
    }, executor);
    CompletableFuture<Void> taskC = CompletableFuture.runAsync(() -> {
      delayRandom(30, 100);
      throw new RuntimeException("task C exception");
    }, executor);
    CompletableFuture.allOf(taskA, taskB, taskC).whenComplete((result, ex) -> {
      if (ex != null) {
        System.out.println(ex.getMessage());
      }
      if (!taskA.isCompletedExceptionally()) {
        System.out.println("task A " + taskA.join());
      }
      if (!taskB.isCompletedExceptionally()) {
        System.out.println("task B ");
      }
    });
  }

  private static final Random rnd = new Random();
  private static ExecutorService executor = Executors.newFixedThreadPool(10);

  static int delayRandom(int min, int max) {
    int milli = max > min ? rnd.nextInt(max - min) : 0;
    try {
      Thread.sleep(min + milli);
    } catch (InterruptedException e) {
    }
    return milli;
  }
}
